package cn.lg.soar.system.account.service.impl;

import cn.lg.soar.common.data.tree.ITreeResult;
import cn.lg.soar.common.util.AssertUtil;
import cn.lg.soar.common.util.ParameterUtil;
import cn.lg.soar.common.util.current.CurrentProxy;
import cn.lg.soar.common.util.current.CurrentUser;
import cn.lg.soar.common.util.current.ThreadLocalHolder;
import cn.lg.soar.common.util.current.UserContext;
import cn.lg.soar.common.util.data.DataUtil;
import cn.lg.soar.common.util.data.StringUtil;
import cn.lg.soar.common.util.data.TreeUtil;
import cn.lg.soar.common.util.token.SoarJWT;
import cn.lg.soar.database.config.DatabaseProperties;
import cn.lg.soar.system.account.model.*;
import cn.lg.soar.system.account.service.IAccountService;
import cn.lg.soar.system.api.enums.MenuTypeEnum;
import cn.lg.soar.system.api.enums.OrgTypeEnum;
import cn.lg.soar.system.api.manager.AccountManager;
import cn.lg.soar.system.api.model.*;
import cn.lg.soar.system.auth.entity.Org;
import cn.lg.soar.system.auth.entity.User;
import cn.lg.soar.system.auth.model.MenuTree;
import cn.lg.soar.system.auth.service.*;
import cn.lg.soar.system.monitor.entity.RefreshToken;
import cn.lg.soar.system.monitor.service.IRefreshTokenService;
import cn.lg.soar.system.tenant.entity.Tenant;
import cn.lg.soar.system.tenant.service.ITenantService;
import com.baomidou.mybatisplus.core.toolkit.CollectionUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.transaction.annotation.Transactional;

import java.util.Collections;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import java.util.stream.Collectors;

/**
 * @author luguoxiang
 * 开源项目：https://gitee.com/lgx1992/lg-soar 求star！请给我star！请帮我点个star！
 */
public class AccountServiceImpl implements IAccountService, CurrentProxy<IAccountService> {

    @Autowired
    private DatabaseProperties properties;
    @Autowired
    private IUserService userService;
    @Autowired
    private IOrgService orgService;
    @Autowired
    private IMenuService menuService;
    @Autowired
    private AccountManager accountManager;
    @Autowired
    private RoleMenuService roleMenuService;
    @Autowired
    private ITenantService tenantService;
    @Autowired
    private IAccountBindService accountBindService;
    @Autowired
    private IRefreshTokenService refreshTokenService;

    @Override
    public void updatePassword(UpdatePasswordDTO userPassword) {
        User user = this.userService.getById(UserContext.getUserId());
        ParameterUtil.isTrue(this.userService.verifyPassword(user.getId(), userPassword.getOldPassword()), "密码不正确");
        this.userService.updatePassword(userPassword.getNewPassword(), user.getId());
    }

    @Override
    public void updateInfo(UpdateUserInfoDTO userInfo) {
        boolean flag = false;
        User user = new User();
        if (!StringUtil.isBlank(userInfo.getAvatar())) {
            user.setAvatar(userInfo.getAvatar());
            flag = true;
        }
        if (!StringUtil.isBlank(userInfo.getNickname())) {
            user.setNickname(userInfo.getNickname());
            flag = true;
        }
        AssertUtil.isTrue(flag, "无有效信息，更新失败");
        user.setId(UserContext.getUserId());
        AssertUtil.isTrue(userService.updateById(user), "更新个人信息失败");
    }

    @Override
    public AccountInfoVo getAccountInfo(AccountInfoQueryEnum[] queryEnums, String...systems) {
        AccountInfoVo accountInfoVo = new AccountInfoVo();
        accountInfoVo.setSuperAdministrator(accountManager.isSuperAdministrator());
        for (AccountInfoQueryEnum queryEnum : queryEnums) {
            switch (queryEnum) {
                case user:
                    accountInfoVo.setUser(accountManager.getUser());
                    break;
                case role:
                    accountInfoVo.setRoles(roles().stream().map(AuthRole::getCode).distinct().collect(Collectors.toList()));
                    break;
                case menu:
                    accountInfoVo.setMenus(menus(systems));
                    break;
                case permit:
                    accountInfoVo.setPermits(permits(systems));
                    break;
                default: break;
            }
        }
        return accountInfoVo;
    }

    @Override
    public Set<String> permits(String... systems) {
        List<MenuTree> menus = menus(systems);
        Set<String> permits = new HashSet<>(1024);
        TreeUtil.forEach(menus, (x) -> {
            permits.add(x.getCode());
        });
        return permits;
    }

    @Override
    public List<MenuTree> menus(String...systems) {
        List<MenuTree> tree = menuService.tree();
        if (!accountManager.isSuperAdministrator()) {
            // 角色权限关联
            Long userId = UserContext.getUserId();
            List<Long> roleIds = userService.getUserCurrentAllRoleIds(userId);
            Set<Long> set = new HashSet<>(roleIds.size() << 2);
            for (Long roleId : roleIds) {
                set.addAll(roleMenuService.getMenuIds(roleId));
            }
            tree = ITreeResult.filter(
                    tree,
                    (x) -> set.contains(x.getId()) || CollectionUtils.isNotEmpty(x.getChildren())
            );
        }
        // 系统过来
        if (DataUtil.isEmpty(systems)) {
            return tree;
        }
        Set<String> set = DataUtil.arrayToSet(systems);
        return tree.stream().filter(x -> {
            String system = x.getCode();
            if (!MenuTypeEnum.system.eq(x.getType())) {
                system = "default";
            }
            return set.contains(system);
        }).collect(Collectors.toList());
    }

    @Override
    public List<AuthRole> roles() {
        return accountManager.getRoleList();
    }

    @Override
    public List<AuthOrg> orgs() {
        return accountManager.getOrgList();
    }

    @Override
    public List<AuthDept> depts() {
        List<AuthOrg> orgList = accountManager.getOrgList();
        Set<Long> ids = orgList.stream()
                .filter(x -> OrgTypeEnum.department.equals(x.getType()))
                .map(AuthOrg::getId)
                .collect(Collectors.toSet());
        List<List<Org>> ancestorList = orgService.getAncestorListByIds(ids);
        List<AuthDept> depts = ancestorList.stream().map(orgs -> {
            AuthDept dept = new AuthDept();
            for (Org org : orgs) {
                if (dept.getDeptId() == null && OrgTypeEnum.department.equals(org.getType())) {
                    dept.setDeptId(org.getId());
                    dept.setDeptName(org.getName());
                    dept.setDeptAlias(org.getAlias());
                }
                if (dept.getCompanyId() == null && OrgTypeEnum.company.equals(org.getType())) {
                    dept.setCompanyId(org.getId());
                    dept.setCompanyName(org.getName());
                    dept.setCompanyAlias(org.getAlias());
                }
                if (dept.getCompanyGroupId() == null && OrgTypeEnum.company_group.equals(org.getType())) {
                    dept.setCompanyGroupId(org.getId());
                    dept.setCompanyGroupName(org.getName());
                    dept.setCompanyGroupAlias(org.getAlias());
                }
            }
            return dept;
        }).collect(Collectors.toList());

        return depts;
    }

    @Override
    public List<AuthPost> posts() {
        return accountManager.getPostList();
    }

    @Override
    public List<AuthAccountBind> accounts() {
        return accountManager.getAccountList();
    }

    @Override
    public List<Tenant> tenants() {
        if (properties.getEnabledTenant()) {
            return tenantService.listBuUserId(UserContext.getUserId());
        }
        return Collections.emptyList();
    }

    @Transactional(rollbackFor = Exception.class)
    @Override
    public String changeTenant(ChangeTenantVO vo) {
        Integer tenantId = vo.getTenantId();
        String refreshToken = vo.getRefreshToken();
        // 验证token
        RefreshToken tokenEntity = refreshTokenService.verify(refreshToken);
        // 验证多租户功能是否开启
        AssertUtil.isTrue(properties.getEnabledTenant(), "未启用多租户功能");
        // 解析token
        Object[] payload = SoarJWT.parsePayload(refreshToken, Object[].class);
        CurrentUser currentUser = CurrentUser.valueOf(payload);
        ThreadLocalHolder.setCurrentUser(currentUser);
        boolean rememberMe = Boolean.TRUE.equals(payload[2]);
        Long userId = tokenEntity.getUserId();
        // 验证用户是否属于该租户
        Set<Integer> set = tenantService.listBuUserId(userId)
                .stream()
                .map(Tenant::getId)
                .collect(Collectors.toSet());
        AssertUtil.isTrue(set.contains(tenantId), "你不属于该租户，无法切换");
        // 切换租户
        User user = new User();
        user.setId(userId);
        user.setCurrentTenantId(tenantId);
        userService.updateById(user);
        // 清空用户缓存
        userService.clearCache(userId);
        // 生成新的token
        return refreshTokenService.create(userId, rememberMe);
    }

    @Override
    public void unbindAccount(UnbindAccountVO vo) {
        accountBindService.unbind(vo.getAccountType(), vo.getAccount(), UserContext.getUserId());
    }
}
